/*
	WiSpy - A flash based spy camera application for AIR
    Copyright (C) 2009  Paul Milham

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package {
	//Alchemy jpeg encoder
	//http://segfaultlabs.com/blog/post/asynchronous-jpeg-encoding
	import cmodule.jpegencoder.CLibInit;
	
	import flash.desktop.NativeApplication;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.NativeWindow;
	import flash.display.NativeWindowInitOptions;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.filters.BlurFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	import flash.utils.setInterval;
	
	

	public class WiSpy extends Sprite { 
		
		private static const UPDATE_INTERVAL:uint = 100;
		private static const SCALE_SIZE:uint = 100;
		private static const THRESHOLD:uint = (SCALE_SIZE*SCALE_SIZE)*.01;
		private static const TEMPLATE_INTERVAL:uint = 5000;
		private static const SAVE_FILE_INTERVAL:uint = 200;
		
		
		
		private var _cam:Camera;
		private var _camVid:Video;
		private var _templateFrame:BitmapData;
		private var _curFrame:BitmapData;
		private var _saveFileTimer:Timer = new Timer(SAVE_FILE_INTERVAL, 5);
		
		private var cameraDiffBitmap:Bitmap = new Bitmap();
		 
		
		public function WiSpy() {
			NativeApplication.nativeApplication.autoExit = true;
			
			var win:NativeWindow = new NativeWindow(new NativeWindowInitOptions());
			win.width = 640;
			win.height = 480;
			
			win.activate();
			win.addEventListener(Event.CLOSE, onClose);
		 
			win.stage.addChild(this);
		 
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
		 
		 	initCam();
			
			var initTimer:Timer = new Timer(2000, 1);
			initTimer.addEventListener(TimerEvent.TIMER, init);
			initTimer.start();
		}
		
		private function init(event:TimerEvent):void {
			setInterval(update, UPDATE_INTERVAL);
			setInterval(updateTemplate, TEMPLATE_INTERVAL);
			_saveFileTimer.addEventListener(TimerEvent.TIMER, saveFile);
		}
		
		private function updateTemplate():void
		{  
			_templateFrame = applyMotionFilters(getCamData());
		}
		
		private function initCam():void {
			_cam = Camera.getCamera();
			_cam.setMode(640, 480, 15);
			_camVid = new Video(_cam.width, _cam.height);
			_camVid.attachCamera(_cam);
			addChild(_camVid);
			addChild(cameraDiffBitmap);
			
		}
		
		private function update():void {
			var camBitmapData:BitmapData = applyMotionFilters(getCamData());
			//init data			 
			if (_curFrame == null)
				_curFrame = camBitmapData;
			if (_templateFrame == null)
				_templateFrame = camBitmapData;
			
			//update data 
			_curFrame = camBitmapData;
			
			//compare data
			if (compareFrames(_curFrame)) {
				if(!_saveFileTimer.running){
					_saveFileTimer.reset();
					_saveFileTimer.start();
					saveFile(null);
				}
			}
		}
		
        	 
        private function drawScaled(src:BitmapData, WIDTH:Number, HEIGHT:Number):BitmapData {
		    var m:Matrix = new Matrix();
		    m.scale(WIDTH / src.width, HEIGHT / src.height);
		    var obj:DisplayObject = new Bitmap(src);
		    var bmp:BitmapData = new BitmapData(WIDTH, HEIGHT, false, 0);
		    bmp.draw(obj, m);
		    return bmp;
		}
		
		private function getCamData(w:int=SCALE_SIZE, h:int=SCALE_SIZE):BitmapData
		{
		    var m:Matrix = new Matrix();
		    m.scale(w / _cam.width, h / _cam.height);
			var camBitmapData:BitmapData = new BitmapData(w, h, false, 0);
			camBitmapData.draw(_camVid, m);
			return camBitmapData;
		}
		
		private function createFileName():String {
			var date:Date = new Date();
			var fileName:String = date.toUTCString() + date.milliseconds;
			var index:int = fileName.indexOf(" ");
			while (index != -1) {
				fileName = fileName.substr(0, index) + "_" + fileName.substr(index + 1);
				index = fileName.indexOf(" ");
			}
			
			index = fileName.indexOf(":");
			while (index != -1) {
				fileName = fileName.substr(0, index) + "_" + fileName.substr(index + 1);
				index = fileName.indexOf(":");
			}
			
			return fileName;
		}
		
		private function onClose(event:Event):void {
			NativeApplication.nativeApplication.exit(0);
		}
		
		private function saveFile(e:*):void
		{
			saveToHD2(getCamData(_cam.width/2, _cam.height/2));
		}
		 
  		private function compareFrames(frameOne:BitmapData):Boolean {
  			
  			var diffBitmapData:BitmapData = new BitmapData(frameOne.width, frameOne.height, false, 0);
			diffBitmapData.draw( frameOne, new Matrix() );
			diffBitmapData.draw( _templateFrame, null, null, BlendMode.DIFFERENCE );
			diffBitmapData.threshold( diffBitmapData, diffBitmapData.rect, new Point(), '>', 0xFF222222, 0xFF00FF00,0x00FFFFFF); 
			
			cameraDiffBitmap.bitmapData = diffBitmapData;
			
			var r:Rectangle = diffBitmapData.getColorBoundsRect(0xFFFFFF, 0x0000FF00, true);
			return (r.width * r.height) > THRESHOLD;
  			
  		}
  		 
  		private function applyMotionFilters(data:BitmapData):BitmapData
  		{ 
            data.applyFilter(data, data.rect, new Point(0,0), new BlurFilter(5,5));
			//TODO: any other filters?
           
            return data;
  		}
  		
		private function saveToHD2(bitmapData:BitmapData):void  
       { 
		    var init:CLibInit = new CLibInit();
		    var lib:Object = init.init();
		    var byteArray:ByteArray = new ByteArray();
		    var byteArrayOut:ByteArray = new ByteArray();
		    byteArray = bitmapData.getPixels(bitmapData.rect);
		    byteArray.position = 0;
		    lib.encode(byteArray, byteArrayOut, bitmapData.width, bitmapData.height, 80);
		    
		    var newImage:File = File.desktopDirectory.resolvePath("Images/" + createFileName() + ".jpg");
	        var fileStream:FileStream = new FileStream();
	        try {
	          	fileStream.openAsync(newImage, FileMode.UPDATE);
	           	fileStream.writeBytes(byteArrayOut);
	        }
	        catch (error:Error) {
	          	trace(error); 
	        }                     
       }
	}
}
